/*
 * SourceClass.java
 *
 * Created on 4 de junio de 2007, 11:51 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package pruebas;

import ig.util.KeyGenerator;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.StringTokenizer;

/**
 *
 * @author jsanchez
 */
public class SourceClass {
    private StringBuilder imports=new StringBuilder();
    private StringBuilder constructores=new StringBuilder();
    private StringBuilder atributos=new StringBuilder();
    private StringBuilder metodos=new StringBuilder();
    private StringBuilder implementacionesDeClases=new StringBuilder();
    private String nombreClase=new String();
    private String paquete=new String();
    private String superClase=new String();
    
    String paqueteOut="";
    String dirOutTmo="";
    String PathFullOut="";
    
    public void bulidClase(String dir) throws IOException{
        java.io.File Dir1=null;
        java.io.File Dir2=null;
        if(paquete.length()<=0)
            throw new IOException("Debe existir un paquete..");
        String dirOut="";
        PathFullOut="";
        
        if(dir.trim().length()>0){
            StringTokenizer token=new StringTokenizer(dir,".");
            while(token.hasMoreTokens()){
                if(dirOut.trim().length()>0)
                    dirOut+="/";
                dirOut+=token.nextToken();
            }
            Dir1=new java.io.File(dirOut);
            Dir1.mkdirs();
        }
        dirOutTmo=dirOut;
        
        if(paquete.trim().length()>0){
            StringTokenizer token=new StringTokenizer(paquete,".");
            while(token.hasMoreTokens()){
                if(dirOut.trim().length()>0)
                    dirOut+="/";
                dirOut+=token.nextToken();
            }
            PathFullOut=dirOut.length()>0?dirOut+"/"+paqueteOut:paqueteOut;
            Dir2=new java.io.File(PathFullOut);
            Dir2.mkdirs();
        }
        
        FileWriter write=null;
        PathFinal=dirOut.length()>0?dirOut+"/"+paqueteOut:paqueteOut;
        write=new FileWriter(new File(PathFinal+"/"+nombreClase+".java"));
        write.write(this.toString());
        write.flush();
        write.close();
        try {Thread.sleep(500);} catch (Exception e) {}
    }
    String PathFinal=null;
    
    
    public Object getInstanciaDeLaClase() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException{
        if(!new File(nombreClase+".jar").exists()){
            Runtime JAVAC=Runtime.getRuntime();
            Process p=JAVAC.exec("javac "+PathFinal+"/"+nombreClase+".java");
            int contador=0;
            System.out.println("Waiting... class");
            while(!new File(PathFinal+"/"+nombreClase+".class").exists()){
                try {Thread.sleep(500);} catch (Exception e) {}
                contador++;
                if(contador==16)
                    break;
            }
            
            JAVAC.exec("jar cvf "+nombreClase+".jar "+PathFullOut+"/"+nombreClase+".class");
            String file=new StringTokenizer(paquete,".").nextToken();
            contador=0;
            System.out.println("Waiting... jar");
            while(!new File(nombreClase+".jar").exists()){
                try {Thread.sleep(3000);} catch (Exception e) {}
                contador++;
                if(contador==16)
                    break;
            }
            if(new File(nombreClase+".jar").exists()){
                URLClassLoader cargadorJVM = (URLClassLoader)ClassLoader.getSystemClassLoader();
                String archivoJar=new File(nombreClase+".jar").getAbsolutePath();
                Class claseJVM = URLClassLoader.class;
                Class params[] = new Class[]{ URL.class };
                
                try{
                    File objFile = new File(archivoJar);
                    Method metodo = claseJVM.getDeclaredMethod("addURL", params);
                    
                    //esta linea es para volver accesible el método
                    metodo.setAccessible(true);
                    URL objUrl = objFile.toURL();
                    metodo.invoke(cargadorJVM, new Object[]{ objUrl });
                }catch(Exception th){
                    th.printStackTrace();
                }
//                this.delete(new File(file));
            }
        }
        return Class.forName(this.paquete+"."+nombreClase).newInstance();
    }
    
    private void delete(File file){
        while(file.isDirectory()){
            if(file.listFiles().length>0){
                for (int i = 0; i < file.listFiles().length; i++) {
                    this.delete(file.listFiles()[i]);
                }
            }else
                file.delete();
        }
        if(file.isFile())
            file.delete();
    }
    
    public void addImport(String str){
        imports.append("import "+str+";\n");
    }
    
    public void addInterfaces(String str){
        if(implementacionesDeClases.toString().length()==0 && str.trim().length()>0)
            implementacionesDeClases.append(str);
        else
            implementacionesDeClases.append(", "+str);
    }
    
    public void addConstructor(String str){
        constructores.append(str+"\n");
    }
    
    public void addAtributos(String str,String classs,boolean isPrivate,String valueDefault){
        str=str.trim();
        atributos.append(isPrivate?"    private "+classs+" "+str+"="+valueDefault+";\n":classs+" "+str+"="+valueDefault+";\n");
        if(isPrivate){
            String strTMP=str.substring(0,1).toUpperCase()+str.substring(1,str.length());
            String met="\n    public void set"+strTMP+"("+classs+" "+str+"){\n"+
                    "        this."+str+"="+str+";\n"+
                    "    }\n";
            this.metodos.append(met);
            
            met="\n    public "+classs+" get"+strTMP+"(){\n"+
                    "        return this."+str+";\n"+
                    "    }\n";
            
            this.metodos.append(met);
        }
    }
    
    public void addMetodos(String str){
        imports.append(str+"\n");
    }
    
    public String getClase(){
        return this.getPaquete()+
                this.imports+
                this.getNombreClase()+
                this.getSuperClase()+
                this.getImplementacionesDeClases()+
                this.atributos+
                this.constructores+
                this.metodos+
                "\n}";
    }
    
    public Object invocarMetodo(Object obj,String nameMeotodo,Object arg) throws IllegalAccessException, InvocationTargetException{
        Object[] arg_=new Object[0];
        if(arg!=null){
            arg_=new Object[1];
            arg_[0]=arg;
        }
        System.out.println("metodo invocado:"+nameMeotodo);
        System.out.println("arg:"+arg_[0].getClass().getName());
        for (int i = 0; i < obj.getClass().getMethods().length; i++) {
            if(obj.getClass().getMethods()[i].getName().equals(nameMeotodo)){
                return obj.getClass().getMethods()[i].invoke(obj,arg_);
            }
        }
        return null;
    }
    
    public String toString() {
        return getClase();
    }
    
    public String getPaquete() {
        return "package "+paquete+";\n";
    }
    
    public void setPaquete(String paquete) {
        this.paquete = paquete;
    }
    
    public String getSuperClase() {
        return !superClase.trim().equals("")?" extends "+superClase:"";
    }
    
    public void setSuperClase(String superClase) {
        this.superClase = superClase;
    }
    
    public String getNombreClase() {
        return "public class "+nombreClase;
    }
    
    public void setNombreClase(String nombreClase) {
        this.nombreClase = nombreClase+"_"+Math.abs(KeyGenerator.getInstance().getNewKey());
    }
    
    private String getImplementacionesDeClases() {
        return !implementacionesDeClases.toString().trim().equals("")?" implements "+implementacionesDeClases.toString()+"{\n\n":"{\n\n";
    }
    
    private void setImplementacionesDeClases(StringBuilder implementacionesDeClases) {
        this.implementacionesDeClases = implementacionesDeClases;
    }
}
